上一篇使用 *ngComponentOutlet
的方式來動態產生元件,本篇要跟大家分享的是 ViewContainerRef
,在程式中產生元件實體後再嵌入到畫面上,好處是畫面上的所有元件動作都可以交由程式來控管,配合複雜的業務邏輯可以同時產生多個元件嵌入到同一個 ViewContainer,而抽象類 ViewContainerRef
是 Angular 提供的,它包含許多方便的 API 讓開發者可以操作 DOM,接下來看一個實際的範例。
src\app\shared\directives\publicity-loader.directive.ts
import { Directive, ViewContainerRef } from '@angular/core';
/**
* ## 廣告格式元件載入器
*
* @export
* @class PublicityLoaderDirective
*/
@Directive({
selector: '[appPublicityLoader]',
})
export class PublicityLoaderDirective {
constructor(public viewContainerRef: ViewContainerRef) {}
}
src\app\shared\shared.define.ts
// 共用指令
import { PublicityLoaderDirective } from './directives/publicity-loader.directive';
export const COMPONENTS = [
...
PublicityLoaderDirective,
];
src\app\publicity\publicity.component.html
<div class="publicity">
<!-- 廣告編輯器 -->
<nb-card class="editor">
...
<div class="editor-body">
<!-- viewContainerRef 動態載入 -->
<ng-container appPublicityLoader></ng-container>
</div>
...
</nb-card>
<!-- 結果預覽 -->
<nb-card class="previewer" *ngIf="showResult">
...
</nb-card>
</div>
src\app\publicity\publicity.component.ts
import { Component, OnInit, ViewChild, ComponentFactoryResolver } from '@angular/core';
import { TextComponent, LinkComponent, HtmlComponent } from '@shared/components';
import { PublicityLoaderDirective } from '@shared/directives/publicity-loader.directive';
/**
* ## 廣告元件
*
* @export
* @class PublicityComponent
* @implements {OnInit}
*/
@Component({
selector: 'app-publicity',
templateUrl: './publicity.component.html',
styleUrls: ['./publicity.component.scss'],
})
export class PublicityComponent implements OnInit {
...
@ViewChild(PublicityLoaderDirective, { static: true }) appPublicityLoader: PublicityLoaderDirective;
/**
* ### 動態生成元件畫面
*
* @param {string} [type='']
* @return {*}
* @memberof PublicityComponent
*/
loadComponent(type = '') {
if (!type) return;
const componentList: any = {
text: TextComponent,
link: LinkComponent,
html: HtmlComponent,
};
const componentFactory = this.publicityFactoryResolver.resolveComponentFactory(componentList[type]);
const viewContainerRef = this.appPublicityLoader.viewContainerRef;
viewContainerRef.clear();
// 動態建立元件
viewContainerRef.createComponent(componentFactory);
}
...
ngOnInit(): void {
this.publicityState = this.publicityService.publicityState$.subscribe((resp) => {
this.editorContent = resp;
});
// 先載入文字廣告格式元件
this.loadComponent('text');
}
...
}
與上一篇結果相同,差別在於三種廣告格式的元件都是動態生成後載入的。
客戶不希望廣告文案拘泥於某種格式,希望能組合現有廣告格式,做多樣化的文案呈現。
src\app\publicity\publicity.component.ts
...
loadComponent(type = '') {
if (!type) return;
const componentList: any = {
text: TextComponent,
link: LinkComponent,
html: HtmlComponent,
};
const componentFactory = this.publicityFactoryResolver.resolveComponentFactory(componentList[type]);
const viewContainerRef = this.appPublicityLoader.viewContainerRef;
// 不使用 clear 方法 viewContainerRef.clear();
// 動態建立元件
viewContainerRef.createComponent(componentFactory);
}
...
publicityState 在答案結果呈現的部份需要自行調整,本篇重點是動態建立元件。
Angular 操作 DOM 抽象類包含 ElementRef
、TemplateRef
、ViewContainerRef
,其中 ElementRef 是元素,TemplateRef 是模板,而 ViewContainerRef 則是視圖的操作。Modern Web Framework 的開發通常需要支援多個平台的多種瀏覽器,透過 Angular 提供的這些方法來操作 DOM 將能滿足 APP 支援多瀏覽器的需求。
Angular 路由模組也是專案中很重要的部份,接下來要介紹路由延遲載入與路由守衛。